package com.util.sql;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.util.configuration.ConfigurationParametersManager;
import com.util.exceptions.InternalErrorException;
import com.util.exceptions.ModelException;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * A utility class to execute plain actions.
 */
public final class PlainActionProcessor {

    private static DataSource dataSource = null;
    
    private static Log log = LogFactory.getLog(PlainActionProcessor.class);
    
    static{
        try{
            dataSource = DataSourceLocator.getDataSource(
                ConfigurationParametersManager.getParameter("Datasource/name"));
        }catch (Exception e){
        	e.printStackTrace();
        }
    }

    private PlainActionProcessor() {}

    public final static Object process(NonTransactionalPlainAction action)
            throws ModelException, InternalErrorException {

        Connection connection = null;
        
        try {
                        
            connection = dataSource.getConnection();            
            
            return action.execute(connection);
            
        } catch (SQLException e) {
            throw new InternalErrorException(e);
        } finally {
           	GeneralOperations.closeConnection(connection);
        }
        
    }    
    
    /**
     * Executes a transactional plain action over a connection with
     * transaction isolation level set to "transaction serializable". If the
     * action throws {@link SQLException}, {@link InternalErrorException},
     * {@link RuntimeException} or {@link Error}, the transaction is 
     * rollbacked.
     */
    public final static Object process(TransactionalPlainAction action)
        throws ModelException, InternalErrorException {
        
        Connection connection = null;
        boolean rollback = false;
        
        try {
        
            /* 
             * Get a connection with isolation level to 
             * "TRANSACTION_SERIALIZABLE" and autocommit to "false".
             *
             * IMPORTANT: Some JDBC drivers require "setTransactionIsolation"
             * to be called before "setAutoCommit".
             */
            connection = dataSource.getConnection();
            log.debug("----------- Inicia transaccion --------------- "+ action.getClass());
            connection.setTransactionIsolation(
                Connection.TRANSACTION_SERIALIZABLE);
            connection.setAutoCommit(false);                
            
            /* Execute action. */
            Object result = action.execute(connection);
            
            /* Commit transaction. */
        	log.debug("----------- Commit transaccion --------------- "+ action.getClass());

            connection.commit();
            
            /* Return "result". */
            return result;
            
        } catch(SQLException e) {
            rollback = true;
            throw new InternalErrorException(e);
        } catch(InternalErrorException e) {
            rollback = true;
            throw e;         
        } catch(RuntimeException e) {
            rollback = true;
            throw e;
        } catch(Error e) {
            rollback = true;
            throw e;
        } finally {
            try {
                if (connection != null) {
                    if (rollback) {
                    	log.debug("----------- Rollback transaccion --------------- "+ action.getClass());
                        connection.rollback();
                    }
                    connection.close();
                }
            } catch (SQLException e) {
                throw new InternalErrorException(e);
            }
        }
                        
    }
    
}
